/**
 * MIT License
 * 
 * Copyright (c) 2024 - present @ ebraid
 * 
 * Email: 1477153217@qq.com
 * QQ: 1477153217
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <kernel/initcall.h>
#include <kernel/system.h>
#include <kernel/kobject.h>
#include <kernel/syslog.h>
#include <kernel/shell.h>
#include <kernel/fdt.h>
#include <drivers/platform.h>
#include <mm/mem.h>
#include <config.h>

#ifdef CONFIG_X86_64
#include <stdlib.h>
#endif


// logo design by https://patorjk.com/software/taag/
#define SYSTEM_START_LOGO    "\
 _    _      _                            _               _               _     _                \r\n\
| |  | |    | |                          | |             | |             (_)   | |               \r\n\
| |  | | ___| | ___ ___  _ __ ___   ___  | |_ ___     ___| |__  _ __ __ _ _  __| |               \r\n\
| |/\\| |/ _ \\ |/ __/ _ \\| '_ ` _ \\ / _ \\ | __/ _ \\   / _ \\ '_ \\| '__/ _` | |/ _` |       \r\n\
\\  /\\  /  __/ | (_| (_) | | | | | |  __/ | || (_) | |  __/ |_) | | | (_| | | (_| |             \r\n\
 \\/  \\/ \\___|_|\\___\\___/|_| |_| |_|\\___|  \\__\\___/   \\___|_.__/|_|  \\__,_|_|\\__,_|    \r\n\
                                                                                                 \r\n\
Copyright (C) 2024 - 2028 @ SHANWEN.LI                                                        \r\n\
"


extern const void *fdt_dts_blob;
extern const unsigned long long fdt_dts_size;


/**
 * @brief parse all fdt device tree node, if the node is a platform device, create a platform device object,
 *        Only the first layer will be parsed as platform devices.
 * @param none
 * @return int 0: success, -1: failed
 */
static int sys_fdt_init(void)
{
    int ret = -1;
    ret = fdt_load(fdt_dts_blob, fdt_dts_size);
    if(ret) {
        SYSLOG_ERROR("fdt_load failed, ret = %d", ret);
        return ret;
    }

    fdt_node_t *root_node = fdt_get_root_node();
    fdt_node_t *child_node;

    fdt_for_each_node_child(root_node, child_node) {
        DEBUG_TRACE("FDT: found device tree node: %s", child_node->name);
        
        const char *status = fdt_read_prop_string(child_node, "status");
        if(status != NULL && strcmp(status, "disabled") == 0) {
            continue;
        }

        const char *compatible = fdt_read_prop_string(child_node, "compatible");
        if(compatible == NULL) {
            continue;
        }
    
        size_t reg = 0;
        if(fdt_read_prop_int(child_node, "reg", &reg)) {
            reg = 0;
        }

        struct platform_device *plat = kmalloc(sizeof(struct platform_device));
        if(plat == NULL) {
            SYSLOG_ERROR("kmalloc failed");
            return -1;
        }

        plat->device.dt_node = child_node;
        plat->device.name = child_node->name;
        plat->io_base = reg;
        plat->device.compatible = compatible;

        if(platform_device_register(plat)) {
            PLATFORM_SYSLOG_ERROR("%s device register failed", plat->device.name);
            return -1;
        }
    }

    return 0;
}


/**
 * @brief  system initialization
 * @param  None
 * @return None
 * @note  This function will to init all memory and kobject, and then call all initcall function.
 */
void eb_system_init(void)
{
    kobj_root_init();

    if(platform_bus_register()) {
        eb_sys_exit(1);
    }

    if(sys_fdt_init()) {
        eb_sys_exit(1);
    }

    /* init all initcall function */
    do_early_initcalls();
    do_initcalls();

    uart_put_string(shell_device(), "\r\n" SYSTEM_START_LOGO "\r\n");    
}


/**
 * @brief  system exit
 * @param  state: exit status
 * @return None
 * @note  This function will exit the program.
 */
void eb_sys_exit(int state)
{
#ifdef CONFIG_X86_64
    exit(0);
#else
    while(state);
#endif
}
